// vim: set filetype=c :
//
// mono-runtime.m: Header file to allow dynamic loading of Mono to
// be transparent from code including the mono-runtime.h header.
//
// Authors:
//   Rolf Bjarne Kvinge <rolf@xamarin.com>
//   Aaron Bockover <abock@xamarin.com>
//
// Copyright 2013-2014 Xamarin Inc.
//
<#@ include file="exports.t4" #>

#ifdef DYNAMIC_MONO_RUNTIME

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <dlfcn.h>

#include "xamarin/main.h"
#include "xamarin/mono-runtime.h"
#include "xamarin/runtime.h"
#include "product.h"
#include "runtime-internal.h"

<# foreach (var export in exports) { #>
<#= export.DotNetIf #>typedef <#= export.ReturnType #> (* <#= export.EntryPoint #>_def) (<#= export.ArgumentSignature #>);
<#= export.DotNetEndIf #><# } #>

<# foreach (var export in exports) { #>
<#= export.DotNetIf #><#= export.EntryPoint #>_def <#= export.EntryPoint #>_func = NULL;
<#= export.DotNetEndIf #><# } #>

char *
xamarin_get_mono_runtime_build_info ()
{
	if (!mono_get_runtime_build_info_func)
		return NULL;
	return mono_get_runtime_build_info_func ();
}

static char xamarin_dynamic_runtime_initialized = 0;

const char *
xamarin_initialize_dynamic_runtime (const char *mono_runtime_prefix)
{
	if (xamarin_dynamic_runtime_initialized)
		return NULL;

	void *libmono = NULL;
	const char *errmsg = NULL;

	// Check if we already have mono available.
	// RTLD_DEFAULT is the catch-all, but it's slow (according to the dlsym man page),
	// so try MAIN_ONLY and SELF first to see if we can optimize a bit.
	if (dlsym (RTLD_MAIN_ONLY, "mono_get_runtime_build_info")) {
		libmono = RTLD_MAIN_ONLY;
	} else if (dlsym (RTLD_SELF, "mono_get_runtime_build_info")) {
		libmono = RTLD_SELF;
	} else if (dlsym (RTLD_DEFAULT, "mono_get_runtime_build_info")) {
		libmono = RTLD_DEFAULT;
	} else if (mono_runtime_prefix != NULL) {
		NSString *dylib = [[NSString stringWithUTF8String: mono_runtime_prefix] stringByAppendingPathComponent: @"/lib/libmonosgen-2.0.dylib"];
		libmono = dlopen ([dylib UTF8String], RTLD_LAZY);
	}

	if (libmono == NULL) {
		const char *dlopen_err = dlerror ();
		if (dlopen_err != NULL)
			PRINT (PRODUCT ": dlopen error: %s", dlopen_err);
		return "This application requires the Mono framework.";
	}


<# foreach (var export in exports) { #>
<#= export.DotNetIf #><#= export.RuntimeIf #>	<#= export.EntryPoint #>_func = (<#= export.EntryPoint #>_def) dlsym (libmono, "<#= export.EntryPoint #>");
<# if (!export.Optional) { #>
	if (<#= export.EntryPoint #>_func == NULL) {
		fprintf (stderr, "Could not load <#= export.EntryPoint #>\n");
		errmsg = "Failed to load the Mono framework.";
	}
<# } #>
<#= export.DotNetEndIf #><#= export.RuntimeEndIf #>
<# } #>
	xamarin_dynamic_runtime_initialized = 1;

	return errmsg;
}

<# foreach (var export in exports) { #>
<#= export.DotNetIf #>MONO_API <#= export.ReturnType #>
<#= export.EntryPoint #> (<#= export.ArgumentSignature #>)
{
<# if (export.Optional) { #>
	if (<#= export.EntryPoint #>_func == NULL)
<# if (!string.IsNullOrEmpty (export.AlternativeExpression)) { #>
		<#= export.AlternativeExpression #>
<# } else {#>
		xamarin_assertion_message ("Could not load <#= export.EntryPoint #>\n");
<# } #>

<# } #>
	return <#= export.EntryPoint #>_func (<#= export.ArgumentNames #>);
}
<#= export.DotNetEndIf #><# } #>

<# foreach (var export in exports) {
	if (!export.Optional)
		continue; #>
bool
<#= export.EntryPoint #>_exists ()
{
	return <#= export.EntryPoint #>_func != NULL;
}

<# } #>
#else
int xamarin_fix_ranlib_warning_about_no_symbols;
#endif /* DYNAMIC_MONO_RUNTIME */

#if defined (CORECLR_RUNTIME)
#include "xamarin/runtime.h"

<# foreach (var export in exports) {
	if (export.XamarinRuntime == RuntimeMode.MonoVM)
		continue;
	if (export.HasCoreCLRBridgeFunction)
		continue;
#>
MONO_API <#= export.ReturnType #>
<#= export.EntryPoint #> (<#= export.ArgumentSignature #>)
{
	xamarin_assertion_message ("The method %s has not been implemented for CoreCLR.\n", __func__);
}

<# } #>
#endif // CORECLR_RUNTIME
